/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.biz.modular.qhstatistics.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.xiaonuo.biz.modular.qhhistory.entity.QhHistory;
import vip.xiaonuo.biz.modular.qhhistory.enums.FutureProducts;
import vip.xiaonuo.biz.modular.qhhistory.service.QhHistoryService;
import vip.xiaonuo.biz.modular.qhstatistics.entity.QhStatistics;
import vip.xiaonuo.biz.modular.qhstatistics.mapper.QhStatisticsMapper;
import vip.xiaonuo.biz.modular.qhstatistics.param.*;
import vip.xiaonuo.biz.modular.qhstatistics.service.QhStatisticsService;
import vip.xiaonuo.biz.modular.qhstrategy.entity.QhStrategy;
import vip.xiaonuo.biz.modular.qhstrategy.service.QhStrategyService;
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.page.CommonPageRequest;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 期货统计Service接口实现类
 *
 * @author yoo
 * @date 2024/05/28 10:56
 **/
@Service
@RequiredArgsConstructor
public class QhStatisticsServiceImpl extends ServiceImpl<QhStatisticsMapper, QhStatistics> implements QhStatisticsService {
    private final QhHistoryService qhHistoryService;
    private final QhStrategyService qhStrategyService;

    @Override
    public Page<QhStatistics> page(QhStatisticsPageParam qhStatisticsPageParam) {
        QueryWrapper<QhStatistics> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getTimePos())) {
            queryWrapper.lambda().eq(QhStatistics::getTimePos, qhStatisticsPageParam.getTimePos());
        }
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getContractCode())) {
            queryWrapper.lambda().like(QhStatistics::getContractCode, qhStatisticsPageParam.getContractCode());
        }
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getContractName())) {
            queryWrapper.lambda().like(QhStatistics::getContractName, qhStatisticsPageParam.getContractName());
        }
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getDatasource())) {
            queryWrapper.lambda().like(QhStatistics::getDatasource, qhStatisticsPageParam.getDatasource());
        }
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getHistoryBatchId())) {
            queryWrapper.lambda().eq(QhStatistics::getHistoryBatchId, qhStatisticsPageParam.getHistoryBatchId());
        }
        if (ObjectUtil.isNotEmpty(qhStatisticsPageParam.getStrategyId())) {
            queryWrapper.lambda().eq(QhStatistics::getStrategyId, qhStatisticsPageParam.getStrategyId());
        }
        if (ObjectUtil.isAllNotEmpty(qhStatisticsPageParam.getSortField(), qhStatisticsPageParam.getSortOrder())) {
            CommonSortOrderEnum.validate(qhStatisticsPageParam.getSortOrder());
            queryWrapper.orderBy(true, qhStatisticsPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
                    StrUtil.toUnderlineCase(qhStatisticsPageParam.getSortField()));
        } else {
            queryWrapper.lambda().orderByAsc(QhStatistics::getId);
        }
        return this.page(CommonPageRequest.defaultPage(), queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(QhStatisticsAddParam qhStatisticsAddParam) {
        QhStatistics qhStatistics = BeanUtil.toBean(qhStatisticsAddParam, QhStatistics.class);
        this.save(qhStatistics);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(QhStatisticsEditParam qhStatisticsEditParam) {
        QhStatistics qhStatistics = this.queryEntity(qhStatisticsEditParam.getId());
        BeanUtil.copyProperties(qhStatisticsEditParam, qhStatistics);
        this.updateById(qhStatistics);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<QhStatisticsIdParam> qhStatisticsIdParamList) {
        // 执行删除
        this.removeByIds(CollStreamUtil.toList(qhStatisticsIdParamList, QhStatisticsIdParam::getId));
    }

    @Override
    public QhStatistics detail(QhStatisticsIdParam qhStatisticsIdParam) {
        return this.queryEntity(qhStatisticsIdParam.getId());
    }

    @Override
    public QhStatistics queryEntity(String id) {
        QhStatistics qhStatistics = this.getById(id);
        if (ObjectUtil.isEmpty(qhStatistics)) {
            throw new CommonException("期货统计不存在，id值为：{}", id);
        }
        return qhStatistics;
    }

    @Override
    public void calStopLossRate(QhStatisticsCalParam param) {
        Assert.notBlank(param.getContractCode(), "合约代码不能为空");
        Assert.notNull(param.getTimePos(), "开始统计时间不能为空");
        LambdaQueryWrapper<QhStrategy> strategyQuery = new LambdaQueryWrapper<>();
        strategyQuery.eq(QhStrategy::getUseFlag, "Y");
        QhStrategy strategy = qhStrategyService.getOne(strategyQuery);
        Assert.notNull(strategy, "请先配置策略");
        /*
         *  验证历史数据是否符合策略交易日天数内止损率的统计
         */
        LambdaQueryWrapper<QhHistory> historyVerify = new LambdaQueryWrapper<>();
        historyVerify.eq(QhHistory::getContractCode, param.getContractCode());
        historyVerify.ge(QhHistory::getTradeDate, param.getTimePos());
        List<QhHistory> verifyHistories = qhHistoryService.list(historyVerify);
        if (verifyHistories.size() < strategy.getTradeTimes() * 2 - 1) {
            //todo 策略新增合约代码和合约代码最后交易时间。用于限制
            throw new CommonException("无法统计[合约：{}，策略天数：{}，开始日期：{}]",
                    param.getContractCode(), strategy.getTradeTimes(), param.getTimePos());
        }

        List<Date> timePosList = getHistoryByQhStrategy(param, strategy).stream().map(QhHistory::getTradeDate).collect(Collectors.toList());
        //计算时间范围的每个时刻的止损率
        timePosList.forEach(t -> {
            param.setTimePos(t);
            //todo 加快统计速度
            cal(param, strategy);
        });
    }

    /**
     * 计算最近times交易日天数的每个时刻的止损率
     */
    private void cal(QhStatisticsCalParam param, QhStrategy strategy) {
        List<QhHistory> histories = getHistoryByQhStrategy(param, strategy);
        int size = histories.size();
        Integer dot = strategy.getMoveDot();//初始移动止损点数，相对于首交易日的开盘价
        Integer days = strategy.getMoveDay(); //每隔多少个交易日进行一次止损判定
        Integer times = strategy.getTradeTimes();//交易日天数内
        String contractCode = param.getContractCode();//合约代码
        Date timePos = param.getTimePos();//统计开始时间
        boolean upTrend = true;//是否上涨趋势移动止损，false为下跌移动止损
        int lossTimes = 0;//止损计数

        //止损率统计入库
        String strTimePos = DateUtil.format(param.getTimePos(), "YYYY-MM-DD");
        LambdaQueryWrapper<QhStatistics> statisticsLambdaQueryWrapper = new LambdaQueryWrapper<>();
        statisticsLambdaQueryWrapper.eq(QhStatistics::getContractCode, param.getContractCode());
        statisticsLambdaQueryWrapper.eq(QhStatistics::getTimePos, strTimePos);
        QhStatistics existRecord = getOne(statisticsLambdaQueryWrapper);
        QhStatistics qhStatistics = existRecord == null ? new QhStatistics() : existRecord;
        qhStatistics.setContractCode(param.getContractCode());
        qhStatistics.setDatasource(histories.get(0).getDatesource());
        qhStatistics.setTimePos(DateUtil.format(param.getTimePos(), "YYYY-MM-DD"));
        qhStatistics.setLossTimes(lossTimes);
        qhStatistics.setContractName(FutureProducts.containsAndGet(param.getContractCode()));
        qhStatistics.setTimes(times);
        qhStatistics.setDownLossTimes(null);
        qhStatistics.setUpLossTimes(null);
        qhStatistics.setProfits(null);
        qhStatistics.setLossRate((lossTimes / size) * 1f);
        saveOrUpdate(qhStatistics);
    }

    private List<QhHistory> getHistoryByQhStrategy(QhStatisticsCalParam param, QhStrategy strategy) {
        LambdaQueryWrapper<QhHistory> historyQuery = new LambdaQueryWrapper<>();
        historyQuery.eq(QhHistory::getContractCode, param.getContractCode());
        historyQuery.ge(QhHistory::getTradeDate, param.getTimePos());
        historyQuery.orderByAsc(QhHistory::getTradeDate);
        historyQuery.last("limit " + strategy.getTradeTimes());//查询最近几天的交易
        return qhHistoryService.list(historyQuery);
    }
}
